feat(skills): unify license-file naming and ship a license in every bundle - #47
Conversation
…undle
Migrates the catalogue onto Standard §4.3 naming and §5.6 license carriage
(both landed in v1.50 — Standard#29, Construct#46).
Before: 34 of 44 bundles shipped no license text at all. Nine skills carried
LICENSE.md — markdown-converted GPL, in three non-identical variants, one of
which (spacecraft-texinfo-document) was a hand-written prose page containing
no license text. microsoft-rust-guidelines used the dash form LICENSE-GPL /
LICENSE-MIT. The documented bundle recipe named <name>/LICENSE, which matched
none of the files that actually existed.
After: every one of the 44 shipped skills plus the Grok skill carries a
license file named per §4.3, a regular file, byte-identical to the canonical
text in LICENSES/, and present in both bundles.
43 x LICENSE GPL-3.0-or-later
1 x LICENSE GFDL-1.3-or-later (gnu-coding-standards,
third-party-derived per §4.2)
1 x LICENSE.GPL + LICENSE.MIT microsoft-rust-guidelines, dual-licensed
1 x LICENSE (flat bundle root) grok-skills/gfm-markdown
Which license applies is read from REUSE.toml rather than a hand-kept list.
android-skills/ and orca-skills/ are untouched: §4.2 preserves upstream
layout verbatim, including upstream's own LICENSE.txt.
Adds .github/check-license-files.py and wires it into CI. It verifies naming,
regular-file-ness, byte-equality against LICENSES/, and actual presence inside
both bundles. Negative-tested against six violation classes — missing file,
symlink, one byte of drift, stray LICENSE.md, dash-form LICENSE-MIT, and a
bundle built without the license — each of which it catches.
Adds a root COPYING symlink to LICENSE (§4.3 GNU convention). reuse ignores
it and GitHub's detector still resolves the regular LICENSE at rank 1.00.
ship.rs: the rebuild-hint candidate list becomes a documented BUNDLE_FILES
const covering LICENSE and the LICENSE.<TAG> forms. It previously probed
LICENSE.md and would have dropped microsoft-rust-guidelines' two license
files from the hint — the one skill with a legal need for both.
The texinfo skill's LICENSE.md carried one substantive fact beyond boilerplate
— that the skill's own license is independent of the license of the manuals it
produces — which is preserved in its SKILL.md rather than lost with the file.
Docs updated to match: AGENTS.md, CONTRIBUTING.md, README.md,
grok-skills/README.md, and microsoft-rust-guidelines' SKILL.md / CREDITS.md
links, which pointed at the pre-rename filenames.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SmZmsWzmSaXpS7bFQu3fWJ
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3d981569e6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| skills = [(p.parent, p.parent.name) for p in sorted(repo.glob("*/SKILL.md")) | ||
| if (repo / f"{p.parent.name}.zip").exists()] |
There was a problem hiding this comment.
Check new skills even before their bundles exist
When a new top-level skill is added without <name>.zip, this filter omits it entirely, so the gate reports success instead of detecting the missing license and both missing bundles. This matters because flake.nix auto-detects every top-level directory containing SKILL.md and can therefore distribute that unlicensed skill source despite CI passing; enumerate all non-vendored skill directories and let the later bundle-existence checks report omissions.
AGENTS.md reference: AGENTS.md:L413-L420
Useful? React with 👍 / 👎.
| if entry not in names: | ||
| problems.append(f"{name}{ext}: does not ship {entry} (§5.6)") |
There was a problem hiding this comment.
Validate the license bytes stored in each bundle
If a bundle contains the expected license entry but its contents are stale or corrupted, this check passes because it only tests the entry name; for example, replacing spacecraft-lua-guidelines/LICENSE inside its zip with arbitrary bytes still yields zero license-file problems. Since neither this workflow nor construct skill ship otherwise compares bundle contents, a license-file update can reach consumers with the previous text unless zf.read(entry) is compared with the canonical or working-tree bytes.
AGENTS.md reference: AGENTS.md:L423-L426
Useful? React with 👍 / 👎.
PR #45 edited spacecraft-cli-preference/SKILL.md and references/jaq.md without rebuilding the two bundles that are the install surface, so every consumer installing from the zip would still get the old "near drop-in" claim and the wrong slurp example. Rebuilt both from the rebased branch, so they also carry the LICENSE file that landed in #47. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M94JK8t6zguGCTDP7udiUv
* cli-preference: jaq is not a drop-in for jq
The reference's Gotchas said only that "a handful of exotic jq features aren't
supported" and that `--stream` is missing. Measured against jq 1.8.1 that is a
serious understatement, and the page was recommending a command that silently
does the wrong thing.
The substantive gap is auto-vivification: jq creates missing containers along an
assignment path and jaq does not, so `echo null | jaq '.a.b = 1'` errors where
jq returns {"a":{"b":1}}. Every "build the object as you go" idiom breaks.
Example 6 was `jaq -s 'add' a.json b.json`. jaq slurps per file and runs the
filter once per input, so that command prints two results where the reader
plainly expects one. Corrected to a single file with the divergence called out.
Also records: output-format flags are not last-wins under jaq, 22 jq builtins
are missing, 9 jq flags are rejected, and the arithmetic corners. Adds the
warning that jaq has `-i/--in-place` where jq does not, so an unrecognised flag
must never be passed through blindly.
Points at Pathfinder for the case where existing jq scripts have to keep
working, and marks the §3 mapping-table row as not alias-safe.
The SKILL.md frontmatter description is untouched and remains 986 characters,
within the §5.6 cap.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011r8wNFByLqoc2ZWevWUUMu
* chore(cli-preference): rebuild bundles for the jaq compatibility rewrite
PR #45 edited spacecraft-cli-preference/SKILL.md and references/jaq.md without
rebuilding the two bundles that are the install surface, so every consumer
installing from the zip would still get the old "near drop-in" claim and the
wrong slurp example.
Rebuilt both from the rebased branch, so they also carry the LICENSE file that
landed in #47.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M94JK8t6zguGCTDP7udiUv
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Migrates the catalogue onto Standard §4.3 naming and §5.6 license carriage, both landed in v1.50 (Standard#29, Construct#46). Third and last PR of the series.
Before
.zip/.skilland never see the repo rootLICENSE.md: markdown-converted GPL, in three non-identical variants, one of which (spacecraft-texinfo-document) was a hand-written prose page containing no license textmicrosoft-rust-guidelinesused the dash formLICENSE-GPL/LICENSE-MIT<name>/LICENSE— matching none of the files that actually existedship.rsprobed["LICENSE", "LICENSE.md", "CREDITS.md"], so its rebuild hint dropped both license files from the one skill with a legal need for themAfter
LICENSEGPL-3.0-or-laterLICENSEGFDL-1.3-or-later—gnu-coding-standards, third-party-derived (§4.2)LICENSE.GPL+LICENSE.MITmicrosoft-rust-guidelines, dual-licensedLICENSE(flat bundle root)grok-skills/gfm-markdownEvery file is a regular file, byte-identical to the canonical text in
LICENSES/, and present in both bundles. Which license applies is read fromREUSE.toml, not a hand-kept list.android-skills/andorca-skills/are untouched — §4.2 preserves upstream layout verbatim, including upstream's ownLICENSE.txt. Confirmed by diff: no staged path under either tree.The gate
New
.github/check-license-files.py, wired into CI. It verifies §4.3 naming, regular-file-ness, byte-equality againstLICENSES/, and actual presence inside both bundles.A gate that cannot fail is worthless, so it was negative-tested against six violation classes — all six caught:
LICENSEdeletedmissing LICENSE (§5.6 license carriage; skill is GPL-3.0-or-later)LICENSEmade a symlinkis a symlink; §5.6 requires a regular filenot byte-identical to LICENSES/GPL-3.0-or-later.txt (§5.6)LICENSE.mdnon-compliant license filename (§4.3)LICENSE-MITmissing LICENSE.MITdoes not ship spacecraft-lua-guidelines/LICENSE (§5.6)Also
COPYINGsymlink →LICENSE(§4.3 GNU convention). Verified: git stores it mode120000,reuse lintignores it, and licensee still resolves the regularLICENSEatexact/100.ship.rs: candidate list becomes a documentedBUNDLE_FILESconst coveringLICENSEand theLICENSE.<TAG>forms.SKILL.md.AGENTS.md,CONTRIBUTING.md,README.md,grok-skills/README.md, andmicrosoft-rust-guidelines'SKILL.md/CREDITS.mdlinks, which pointed at the pre-rename filenames.Verification
Run against a clean
git archive HEADcheckout so gitignored local files can't mask a failure:steelbore.scmmatchessteelbore.tomlreuse lintSKILL.mddescription capDRIFTlinescargo fmt --check/clippy -D warnings/cargo testGPL-3.0,exact, 100%G? = G,+0000Note on staging
The repo rule is never
git add -A, because other root.skillfiles can carry pre-existing uncommitted changes. Here every bundle legitimately changes, so I staged in bulk with.gitignoreexcluded — then audited the staged set by category (45.zip, 45.skill, 46 license files, 13 named others) and confirmed nothing fromtarget/,Chat*,Excluded/, or the vendored trees was swept in. The working tree was clean apart from.gitignorebefore this branch, which is what makes that safe; it is not a precedent for ordinary single-skill edits.After merge
~/.claude/skills/and the other per-harness paths refresh only after a Home Manager rebuild — maintainer-run, not automated.construct skill syncin consumer flakes afterwards.